%{
#include "ch3hdr2.h"
#include <string.h>
#include <math.h>
#include <stdio.h>

void yyerror (char const *s);
int yylex();

struct symtab symtab[NSYMS];

%}

%union {
    double dval;
    struct symtab *symp;
}

%token <symp> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type <dval> expression

%%
statement_list: statement '\n'
        | statement_list statement '\n'
        ;

statement: NAME '=' expression { $1->value = $3; }
        | expression { printf("= %g\n", $1); }
        ;

expression: expression '+' expression { $$ = $1 + $3; }
        |   expression '-' expression { $$ = $1 - $3; }
        |   expression '*' expression { $$ = $1 * $3; }
        |   expression '/' expression {
                                        if($3 == 0)
                                            yyerror("divide by zero");
                                        else
                                            $$ = $1 / $3;
                                      }
        |   '-' expression %prec UMINUS { $$ = - $2; }
        |   '(' expression ')'          { $$ = $2; }
        |   NUMBER
        |   NAME                        { $$ = $1->value; }
        |   NAME '(' expression ')'     {
                                            if($1->funcptr)
                                                $$ = ($1->funcptr)($3);
                                            else {
                                                printf("%s not a function\n", $1->name);
                                                $$ = 0.0;
                                            }
                                        }
        ;
%%

struct symtab * symlook(s)
char *s;
{
    char *p;
    struct symtab *sp;
    for (sp = symtab; sp < &symtab[NSYMS]; sp++) {
        if (sp->name && !strcmp(sp->name, s))
            return sp;
        if (!sp->name) {
            sp->name = strdup(s);
            return sp;
        }
    }
    yyerror("Too many symbols");
}

void addfunc(name, func)
char *name;
double (*func)();
{
    struct symtab *sp = symlook(name);
    sp->funcptr = func;
}

int main()
{
    extern double sqrt(), exp(), log();
    addfunc("sqrt", sqrt);
    addfunc("exp", exp);
    addfunc("log", log);
    yyparse();
    return 0;
}

 void yyerror (char const *s) {
   fprintf (stderr, "%s\n", s);
 }
